﻿using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

namespace Generador_Terreno
{
    [ContentProcessor]
    class Generador_Terreno: ContentProcessor<Texture2DContent, ModelContent>
    {
        const float escalaTerreno = 20;
        const float abultamientoTerreno = -500;
        const float escalaTextura = 0.25f;
        const string terrenoTextura = "cesped2.bmp";

        /// <summary>
        /// Genera el terreno a partir del mapa de alturas
        /// </summary>
        /// <param name="input"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public override ModelContent Process(Texture2DContent input, ContentProcessorContext context)
        {
            MeshBuilder constructor = MeshBuilder.StartMesh("terreny");

            // Convertir la textura entrante en un float, para facilitar el procesamiento
            input.ConvertBitmapType(typeof(PixelBitmapContent<float>));

            PixelBitmapContent<float> mapaAlturas;
            mapaAlturas = (PixelBitmapContent<float>)input.Mipmaps[0];

            // Crea los vertices del terreno
            for (int y = 0; y < mapaAlturas.Height; y++)
            {
                for (int x = 0; x < mapaAlturas.Width; x++)
                {
                    Vector3 position;

                    position.X = (x - mapaAlturas.Width / 2) * escalaTerreno;
                    position.Z = (y - mapaAlturas.Height / 2) * escalaTerreno;

                    // Establecer la altura segun el color del pixels
                    position.Y = (mapaAlturas.GetPixel(x, y) - 1) * abultamientoTerreno;

                    constructor.CreatePosition(position);
                }
            }


            // Crea un material y associarla a la textura del terreno
            BasicMaterialContent material = new BasicMaterialContent();

            string directory = Path.GetDirectoryName(input.Identity.SourceFilename);
            string texture = Path.Combine(directory, terrenoTextura);

            material.Texture = new ExternalReference<TextureContent>(texture);

            constructor.SetMaterial(material);

            // Crea un canal de vertice que mantendrà las coordenades de la textura a lloc
            int texCoordId = constructor.CreateVertexChannel<Vector2>(VertexChannelNames.TextureCoordinate(0));

            // Crea cada uno de los triangulos que forman el terreno
            for (int y = 0; y < mapaAlturas.Height - 1; y++)
            {
                for (int x = 0; x < mapaAlturas.Width - 1; x++)
                {
                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x, y);
                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x + 1, y);
                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x + 1, y + 1);

                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x, y);
                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x + 1, y + 1);
                    AddVertex(constructor, texCoordId, mapaAlturas.Width, x, y + 1);
                }
            }

            // Pasar al ModelProcessor que convertirà el mesh que acabamos de generar
            MeshContent meshTerreny = constructor.FinishMesh();

            return context.Convert<MeshContent, ModelContent>(meshTerreny,"ModelProcessor");
        }

        /// <summary>
        /// Helper para añadir un nuevo vértice de un triángulo en el MeshBuilder, asociado a la coordenada de la textura
        /// </summary>
        static void AddVertex(MeshBuilder builder, int texCoordId, int w, int x, int y)
        {
            builder.SetVertexChannelData(texCoordId, new Vector2(x, y) * escalaTextura);

            builder.AddTriangleVertex(x + y * w);
        }
    }
}
